//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>
#include <drvpub.h>
#include "audiocard.h"


class CAudioCard : public Driver {
public:
    CARAPI Read(
        /* [in] */ UINT64 u64Offset,
        /* [in] */ UINT uNumberOfBytesToRead,
        /* [out] */ EzByteBuf ebbData,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Write(
        /* [in] */ UINT64 u64Offset,
        /* [in] */ EzByteBuf ebbData,
        /* [out] */ UINT * puNumberOfBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Control(
        /* [in] */ INT nControlCode,
        /* [in] */ EzByteBuf ebbInData,
        /* [out] */ EzByteBuf * pOutData,
        /* [out] */ IEvent * * ppCompletionEvent);

    virtual void Dispose();

    AudioContext m_audioCxt;
    DzMutex   m_lock;

};

static CAudioCard s_audio;


ECode CAudioCard::Read(
        /* [in] */ UINT64 u64Offset,
        /* [in] */ UINT uNumberOfBytesToRead,
        /* [out] */ EzByteBuf ebbData,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    void *pBuffer = (void *)(char *)ebbData;
    UINT uSizeRead;
    ECode ec;

    WaitResult wr;
    m_lock.Lock(&wr);

    ec = AudioIn_Read(&m_audioCxt, pBuffer, uNumberOfBytesToRead, &uSizeRead);
    if (FAILED(ec)) {
        m_lock.Unlock();
        return ec;
    }

    ebbData.SetUsed(uSizeRead);
    m_lock.Unlock();

    return NOERROR;
}

ECode CAudioCard::Write(
        /* [in] */ UINT64 u64Offset,
        /* [in] */ EzByteBuf ebbData,
        /* [out] */ UINT * puNumberOfBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    void *pBuffer = (void *)(char *)ebbData;
    UINT uSizeWrite = ebbData.GetUsed();
    ECode ec;

    WaitResult wr;
    m_lock.Lock(&wr);

    ec = AudioOut_Write(&m_audioCxt, (void *)pBuffer, uSizeWrite, puNumberOfBytesWritten);
    m_lock.Unlock();

    return ec;
}

ECode CAudioCard::Control(
        /* [in] */ INT nControlCode,
        /* [in] */ EzByteBuf ebbInData,
        /* [out] */ EzByteBuf * pOutData,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    ECode ec = NOERROR;
    WaitResult wr;
    AudioContext *pCxt = &m_audioCxt;

    UINT64 Arg = ebbInData.GetUsed() < sizeof(Uint64) ? 0 : *(UINT64*)(char*)ebbInData;

    m_lock.Lock(&wr);

    switch (nControlCode) {
    case 0: {
        /*
         *  Sample rate & Channels
         *  Format: bit0-bit31 Sample Rate
                    bit32-bit47 Bytes Per Sample
                    bit48-bit63 Number of Channels(1 & 2)
         */
        if (ebbInData.GetCapacity() < (int)sizeof(Audio_Params)) {
            ec = E_OUT_OF_MEMORY;
            break;
        }
        Audio_Params *pParams = (Audio_Params *)(char *)ebbInData;

        m_audioCxt.nRate = pParams->SampleRate;
        m_audioCxt.nBits = (UINT)pParams->BitsPerSample;
        m_audioCxt.nChannels = (UINT)pParams->Channels;;
        DBGOUT(kprintf("Playing:Rate=%d, BitsPerSample=%d, Channels=%d\n",
            m_audioCxt.nRate, m_audioCxt.nBits, m_audioCxt.nChannels));

        ec = Audio_Initial(pCxt, WAVEDIR_PLAYING,
            AUDIO_ALLOCBUFFER | AUDIO_OPENHARDWARE);

        break;
    }

    case 1: /* Master Volume */
        m_audioCxt.volLeftOut = (UINT8)Arg;
        m_audioCxt.volRightOut = (UINT8)Arg;
        if (WAVEDIR_PLAYING == pCxt->waveDir) {
            ec = Codec_SetAudioOutVolume(pCxt,
                    m_audioCxt.volLeftOut, m_audioCxt.volRightOut);
        }
        break;

    case 2: /* Voice Channel Volume(s) */
        m_audioCxt.volLeftOut = (UINT8)Arg;
        m_audioCxt.volRightOut = (UINT8)(Arg >> 8);
        if (WAVEDIR_PLAYING == pCxt->waveDir) {
            ec = Codec_SetAudioOutVolume(pCxt,
                    m_audioCxt.volLeftOut, m_audioCxt.volRightOut);
        }
        break;

    case 3: {// Get playing volume
        if (pOutData->GetCapacity() < (int)sizeof(BYTE) * 2) {
            ec = E_OUT_OF_MEMORY;
            break;
        }

        BYTE *pVolLeftOut = (BYTE *)(char *)ebbOutData;
        BYTE *pVolRightOut = pVolLeftOut + 1;

        *pVolLeftOut = pCxt->volLeftOut;
        *pVolRightOut = pCxt->volRightOut;

        pOutData->SetUsed(sizeof(BYTE) * 2);
        break;
    }

    case 0x10:
        // Flush the buffers and close wave out device
        if (m_audioCxt.waveDir == WAVEDIR_PLAYING) {
            DBGOUT(kprintf("Flush the buffers and close wave out device\n"));
            Audio_FlushBuffer(pCxt);
            Audio_Close(pCxt, TRUE);
        }
        break;

    case 0x11:
        // Reset wave out device
        Audio_Reset(pCxt);
        break;

    case 0x20:
        if ((UINT)Arg == 0) {
            // Open the speaker
            if (!(pCxt->speakerStatus & SPEAKER_OPEN)) {
                pCxt->speakerStatus |= SPEAKER_OPEN;
                if (WAVEDIR_PLAYING == pCxt->waveDir) {
                    Codec_OpenAmplifier(TRUE);
                }
            }
        }
        else if ((UINT)Arg == 1) {
            // Open the headset
            if (!(pCxt->headsetStatus & HEADSET_OPEN)) {
                pCxt->headsetStatus |= HEADSET_OPEN;
                if ((WAVEDIR_PLAYING == pCxt->waveDir)) {
                // Codec_OpenHeadset(TRUE);
                }
            }
        }
        break;

    case 0x21:
        if ((UINT)Arg == 0) {
            // Close the speaker
            if (pCxt->speakerStatus & SPEAKER_OPEN) {
                Codec_OpenAmplifier(FALSE);
                pCxt->speakerStatus &= ~SPEAKER_OPEN;
            }
        }
        else if ((UINT)Arg == 1) {
            // Close the headset
            if (pCxt->headsetStatus & HEADSET_OPEN) {
                //Codec_OpenHeadset(FALSE);
                pCxt->headsetStatus &= ~HEADSET_OPEN;
            }
        }
        break;

    case 0x22:
        // Start vibration
//        GPDR1 |= (1 << 4);
//        GPSR1 |= (1 << 4);
        break;

    case 0x23:
        // Stop vibration.
//        GPDR1 |= (1 << 4);
//        GPCR1 |= (1 << 4);
        break;

    case 0x80: {
        // Recording params
        Audio_Params *pParams = (Audio_Params *)(char *)ebbInData;
        m_audioCxt.nRate = pParams->SampleRate;
        m_audioCxt.nBits = (UINT)pParams->BitsPerSample;
        m_audioCxt.nChannels = (UINT)pParams->Channels;;
        ec = Audio_Initial(pCxt, WAVEDIR_RECORDING, AUDIO_ALLOCBUFFER | AUDIO_OPENHARDWARE);
        DBGOUT(kprintf("Recording:Rate=%d, BitsPerSample=%d, Channels=%d\n",
            m_audioCxt.nRate, m_audioCxt.nBits, m_audioCxt.nChannels));
        break;
    }

    case 0x90:
        //  Close wave in device
        if (m_audioCxt.waveDir == WAVEDIR_RECORDING) {
            Audio_Close(pCxt, TRUE);
        }
        break;

    case 0x91:
        // Reset wave in device
        Audio_Reset(pCxt);
        break;

    case 0xa0: {
        // Select the input source of wave in device
        ec = Codec_SetAudioInSource(pCxt, (WAVEINSOURCE)(Arg & 0xFF));
        break;
    }

    case 0x100: {
        // Get the driver state and capabilities
        if (pOutData->GetCapacity() < (int)sizeof(Audio_Status)) {
            ec = E_OUT_OF_MEMORY;
            break;
        }
        ec = Audio_GetState(pCxt, (Audio_Status *)(char *)ebbOutData);
        break;
    }

    // Power management code
    case 1000:
        if (pCxt->waveDir != WAVEDIR_NOTPREPARED) {
            Audio_FlushBuffer(pCxt); // Should flush all the buffers
            Audio_Close(pCxt, FALSE);
        }
        Codec_Close(pCxt);
        Codec_OpenAmplifier(FALSE);
        break;

    case 1001:
    case 1002:
        if (pCxt->waveDir != WAVEDIR_NOTPREPARED) {
            // Codec_Open is in Audio_Initial
            Audio_Initial(pCxt, pCxt->waveDir, AUDIO_OPENHARDWARE);
        }
        break;

#if (_TEST_TYPE == 41)
    case 4100:
        SetOutIsrTime(ebbOutData);
        break;
    case 4101:
        SetRcvIsrTime(ebbOutData);
        break;
#endif //_TEST_TYPE == 41

    default:
        ec = E_NOT_IMPLEMENTED;
        break;
    }

    m_lock.Unlock();

    return ec;
}

void CAudioCard::Dispose()
{
}

EXTERN IDeviceDriver * CDECL CreateAudio(uint_t uDeviceNo, void *pvParameter)
{
    ECode ec = Audio_Create(&s_audio.m_audioCxt);
    if (FAILED(ec)) return NULL;

    return &s_audio;
}
